
#ifndef RENDER_H
#define RENDER_H


#include <windows.h>
#include <vector>

#include "Triangle.h"
#include "ShapeBuilder.h"



class CameraBasic
{
public:
	CameraBasic()
		:m_BackwardMatrix(new Matrix3D(Matrix3D::I()))
	{
		_build_forward();
	}

	const Matrix3D& get_forward_matrix()const
	{
		return *m_ForwardMatrix;
	}

	const Matrix3D& get_backward_matrix()const
	{
		return *m_BackwardMatrix;
	}

	void set_backward_matrix(const Matrix3D& matrix)
	{
		(*m_BackwardMatrix) = matrix;
		_build_forward();
	}
private:

	void _build_forward()
	{
		assert(m_BackwardMatrix != NULL);
		if ( m_ForwardMatrix == NULL ) m_ForwardMatrix = new Matrix3D();


		Matrix2D axis;
		for(size_t row = 0;row<axis.RowNo();++row)
		for(size_t col = 0;col<axis.ColNo();++col)
		{
			axis(row,col) = (*m_BackwardMatrix)(row,col);
		}
		Vector2D o_point;
		Vector2D single_axis[3];
		for(size_t row = 0;row<o_point.RowNo();++row)
		{
			single_axis[0](row,0) = (*m_BackwardMatrix)(row,0);
			single_axis[1](row,0) = (*m_BackwardMatrix)(row,1);
			single_axis[2](row,0) = (*m_BackwardMatrix)(row,2);
			o_point(row,0) = (*m_BackwardMatrix)(row,3);
		}

		axis = ~axis;

		Matrix3D matrix;
		for(size_t row = 0;row<axis.RowNo();++row)
		for(size_t col = 0;col<axis.ColNo();++col)
		{
			(*m_ForwardMatrix)(row,col) = axis(row,col);
		}
		for(size_t row = 0;row<o_point.RowNo();++row)
		{
			(*m_ForwardMatrix)(row,3) = -(~single_axis[row] * o_point)(0,0);
		}
		(*m_ForwardMatrix)(3,3) = 1;
	}

private:
	SmartPtr<Matrix3D*> m_ForwardMatrix;
	SmartPtr<Matrix3D*> m_BackwardMatrix;
};

struct ClipBasic
{
public:
	bool set_data_by_hybird(Float3D fov_x,Float3D wh_ratio,Float3D near_z,Float3D far_z)
	{
		m_NearZ = near_z;
		m_FarZ = far_z;
		m_FovX = fov_x;
		m_FovY = atan( tan(fov_x/2/180*PI) / wh_ratio) / PI * 360;
		_build_matrix();
		return true;
	}
	bool set_data_by_fov(Float3D fov_x,Float3D fov_y,Float3D near_z,Float3D far_z)
	{
		m_NearZ = near_z;
		m_FarZ = far_z;
		m_FovX = fov_x;
		m_FovY = fov_y;
		_build_matrix();
		return true;
	}
	bool set_data_by_size(Float3D near_width,Float3D near_height,Float3D near_z,Float3D far_z)
	{
		m_NearZ = near_z;
		m_FarZ = far_z;
		m_FovX = atan2( near_width / 2 , near_z ) ;
		m_FovY = atan2( near_height / 2 , near_z ) ;
		_build_matrix();
		return true;
	}
	
	const Matrix3D& get_forward_matrix()const
	{
		return *m_ForwardMatrix;
	}

	const Matrix3D& get_backward_matrix()const
	{
		return *m_BackwardMatrix;
	}

	Float3D get_deep()const
	{
		return m_FarZ - m_NearZ;
	}
	
//private:
	bool _build_matrix()
	{
		

		if ( m_ForwardMatrix == NULL ) m_ForwardMatrix = new Matrix3D();
		if ( m_BackwardMatrix == NULL ) m_BackwardMatrix = new Matrix3D();

		Float3D nz = m_NearZ;
		Float3D fz = m_FarZ;

		if ( is_zero(fz) || is_zero(fz-nz) ) return false;

		{
			(*m_ForwardMatrix)(0,0) = 1/tan(m_FovX/2/180.0*PI);				(*m_ForwardMatrix)(0,1) = 0;		(*m_ForwardMatrix)(0,2) = 0;				(*m_ForwardMatrix)(0,3) = 0;
			(*m_ForwardMatrix)(1,0) = 0;				(*m_ForwardMatrix)(1,1) = 1/tan(m_FovY/2/180.0*PI);		(*m_ForwardMatrix)(0,2) = 0;				(*m_ForwardMatrix)(1,3) = 0;
			(*m_ForwardMatrix)(2,0) = 0;				(*m_ForwardMatrix)(2,1) = 0;		(*m_ForwardMatrix)(2,2) = fz/(fz-nz);				(*m_ForwardMatrix)(2,3) = -fz*nz/(fz-nz);
			(*m_ForwardMatrix)(3,0) = 0;				(*m_ForwardMatrix)(3,1) = 0;		(*m_ForwardMatrix)(3,2) = 1;				(*m_ForwardMatrix)(3,3) = 0;


			//Matrix3D matrix;
			//matrix(0,0) = 1/tan(m_FovX/2/180.0f*PI);		matrix(0,1) = 0;			matrix(0,2) = 0;			matrix(0,3) = 0;
			//matrix(1,0) = 0;				matrix(1,1) = 1/tan(m_FovY/2/180.0f*PI);	matrix(0,2) = 0;			matrix(1,3) = 0;
			//matrix(2,0) = 0;				matrix(2,1) = 0;								matrix(2,2) = fz/(fz-nz);	matrix(2,3) = -fz*nz/(fz-nz);
			//matrix(3,0) = 0;				matrix(3,1) = 0;								matrix(3,2) = 1;			matrix(3,3) = 0;
			//(*m_ForwardMatrix) = matrix * (*m_ForwardMatrix);
		}
		{
			(*m_BackwardMatrix)(0,0) = tan(m_FovX/2/180.0*PI);	(*m_BackwardMatrix)(0,1) = 0;	(*m_BackwardMatrix)(0,2) = 0;		(*m_BackwardMatrix)(0,3) = 0;
			(*m_BackwardMatrix)(1,0) = 0;	(*m_BackwardMatrix)(1,1) = tan(m_FovY/2/180.0*PI);	(*m_BackwardMatrix)(0,2) = 0;		(*m_BackwardMatrix)(1,3) = 0;
			(*m_BackwardMatrix)(2,0) = 0;	(*m_BackwardMatrix)(2,1) = 0;	(*m_BackwardMatrix)(2,2) = 0;							(*m_BackwardMatrix)(2,3) = 1;
			(*m_BackwardMatrix)(3,0) = 0;	(*m_BackwardMatrix)(3,1) = 0;	(*m_BackwardMatrix)(3,2) = -(fz-nz)/(fz*nz);					(*m_BackwardMatrix)(3,3) = 1/nz;

			//Matrix3D matrix;
			//matrix(0,0) = tan(m_FovX/2/180.0f*PI);		matrix(0,1) = 0;			matrix(0,2) = 0;			matrix(0,3) = 0;
			//matrix(1,0) = 0;				matrix(1,1) = tan(m_FovY/2/180.0f*PI);	matrix(0,2) = 0;			matrix(1,3) = 0;
			//matrix(2,0) = 0;				matrix(2,1) = 0;								matrix(2,2) = -(fz-nz)/(fz*nz);	matrix(2,3) = nz;
			//matrix(3,0) = 0;				matrix(3,1) = 0;								matrix(3,2) = 0;			matrix(3,3) = 1;
			//(*m_BackwardMatrix) = matrix * (*m_BackwardMatrix);
		}

		

		return true;
	}

	Float3D m_NearZ;
	Float3D m_FarZ;
	Float3D m_FovX;
	Float3D m_FovY;

	SmartPtr<Matrix3D*> m_ForwardMatrix;
	SmartPtr<Matrix3D*> m_BackwardMatrix;

};

struct Camera
{
	CameraBasic camera;
	ClipBasic clip;
};

typedef std::vector<Triangle> WorldData3D;

struct WorldModel3D
{
	WorldData3D world_data;
	Matrix3D world_coordinate;
	Camera camera_data;

	WorldModel3D()
		:world_coordinate( Matrix3D::I() )
	{
	}

	template<unsigned int TriCount>
	void add_shape(const Shape<TriCount>& shape)
	{
		for(unsigned int i=0;i<TriCount;++i)
		{
			add_triangle(shape.triangle_list[i]);
		}
	}
	void add_triangle(const Triangle& tri)
	{
		Triangle _value = tri;
		_value.transform(world_coordinate);
		world_data.push_back(_value);
	}

	void draw(HDC hdc,int x,int y,int cx,int cy);

	bool read_file(const char* file);

	const Matrix3D& get_clip_matrix(bool forward=true);

};





void TestRender(HDC hdc,int x,int y,int cx,int cy);















#endif